﻿// -----------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition.AttributedModel;
using System.Linq;
using System.Reflection;
using Microsoft.Internal;
using System.ComponentModel.Composition.ReflectionModel;

namespace System.ComponentModel.Composition.Caching.AttributedModel
{
    internal class CachedAttributedComposablePartDefinition : ReflectionComposablePartDefinition
    {
        private readonly Func<IEnumerable<ImportDefinition>> _importsCreator;
        private readonly Func<IEnumerable<ExportDefinition>> _exportsCreator;
        private readonly Func<CachedAttributedComposablePartDefinition, ReflectionComposablePart> _partCreator;
        private IEnumerable<ImportDefinition> _imports;
        private IEnumerable<ExportDefinition> _exports;        

        public CachedAttributedComposablePartDefinition(
            IDictionary<string, object> cache,
            Func<CachedAttributedComposablePartDefinition, ReflectionComposablePart> partCreator,
            Func<IEnumerable<ImportDefinition>> importsCreator,
            Func<IEnumerable<ExportDefinition>> exportsCreator)
        {
            Assumes.NotNull(cache);
            Assumes.NotNull(partCreator);
            Assumes.NotNull(importsCreator);
            Assumes.NotNull(exportsCreator);

            this.Cache = cache;
            this._importsCreator = importsCreator;
            this._exportsCreator = exportsCreator;
            this._partCreator = partCreator;
        }

        public IDictionary<string, object> Cache 
        { 
            get; 
            private set; 
        }

        public override IEnumerable<ExportDefinition> ExportDefinitions
        {
            get
            {
                if (this._exports == null)
                {
                    this._exports = this._exportsCreator.Invoke();
                    if (this._exports == null)
                    {
                        this._exports = Enumerable.Empty<ExportDefinition>();
                    }
                }
                return this._exports;
            }
        }

        public override IEnumerable<ImportDefinition> ImportDefinitions
        {
            get
            {
                if (this._imports == null)
                {
                    this._imports = this._importsCreator.Invoke();
                    if (this._imports == null)
                    {
                        this._imports = Enumerable.Empty<ImportDefinition>();
                    }
                }
                return this._imports;
            }
        }

        public override IDictionary<string, object> Metadata
        {
            get { return this.Cache.ReadDictionary<object>(AttributedCacheServices.CacheKeys.Metadata); }
        }

        public override ComposablePart CreatePart()
        {
            return this._partCreator(this);
        }

        public override Type PartType
        {
            get { return (Type)LoadMemberFromCache(AttributedCacheServices.CacheKeys.Type); }
        }

        public override ConstructorInfo Constructor
        {
            get { return (ConstructorInfo)LoadMemberFromCache(AttributedCacheServices.CacheKeys.Constructor); }
        }

        public static IDictionary<string, object> CreateCache(AttributedComposablePartDefinition partDefinition)
        {
            Assumes.NotNull(partDefinition);

            IDictionary<string, object> cache = new Dictionary<string, object>();
            cache.WriteDictionary(AttributedCacheServices.CacheKeys.Metadata, partDefinition.Metadata);
            cache.WriteDictionary(AttributedCacheServices.CacheKeys.Type, CreateCache(partDefinition.PartType));

            // Parts containing only static exports will not have constructors
            if (partDefinition.Constructor != null)
            {   
                cache.WriteDictionary(AttributedCacheServices.CacheKeys.Constructor, CreateCache(partDefinition.Constructor));
            }

            return cache;
        }

        private MemberInfo LoadMemberFromCache(string key)
        {
            IDictionary<string, object> cache = this.Cache.ReadDictionary<object>(key);
            if (cache.Count == 0)
            {
                return null;
            }

            return cache.LoadMember();
        }

        private static IDictionary<string, object> CreateCache(MemberInfo member)
        {
            IDictionary<string, object> cache = new Dictionary<string, object>();
            cache.WriteMember(member);

            return cache;
        }
    }
}
